package com.cbgolf.oa.widget.pickview.lib;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import com.cbgolf.oa.R;
import com.cbgolf.oa.util.DensityUtil;
import com.cbgolf.oa.util.Util;
import com.cbgolf.oa.widget.pickview.adapter.WheelAdapter;
import com.cbgolf.oa.widget.pickview.listener.OnItemSelectedListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;


/**
 * 3d滚轮控件
 */
public class WheelView extends View {

  public enum ACTION {
    // 点击，滑翔(滑到尽头)，拖拽事件
    CLICK, FLING, DAGGLE
  }

  Context context;

  Handler handler;
  private GestureDetector gestureDetector;
  OnItemSelectedListener onItemSelectedListener;

  // Timer mTimer;
  ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor();
  private ScheduledFuture<?> mFuture;

  Paint paintOuterText;
  Paint paintCenterText;
  Paint paintIndicator;

  WheelAdapter adapter;

  private String label;// 附加单位
  int textSize;
  int maxTextWidth;
  int maxTextHeight;
  float itemHeight;// 每行高度

  int textColorOut;
  int textColorCenter;
  int dividerColor;

  // 条目间距倍数
  static final float lineSpacingMultiplier = 3.5F;
  boolean isLoop;

  // 第一条线Y坐标值
  float firstLineY;
  // 第二条线Y坐标
  float secondLineY;
  // 中间Y坐标
  float centerY;

  // 滚动总高度y值
  int totalScrollY;
  // 初始化默认选中第几个
  int initPosition;
  // 选中的Item是第几个
  private int selectedItem;
  int preCurrentIndex;
  // 滚动偏移值,用于记录滚动了多少个item
  int change;

  // 显示几个条目
  int itemsVisible = 9;

  int measuredHeight;
  int measuredWidth;

  // 半圆周长
  int halfCircumference;
  // 半径
  int radius;

  private int mOffset = 0;
  private float previousY = 0;
  long startTime = 0;

  // 修改这个值可以改变滑行速度
  private static final int VELOCITYFLING = 5;
  int widthMeasureSpec;

  private int mGravity = Gravity.CENTER;
  private int drawCenterContentStart = 0;// 中间选中文字开始绘制位置
  private int drawOutContentStart = 0;// 非中间文字开始绘制位置
  private static final float SCALECONTENT = 1.0F;// 非中间文字则用此控制高度，压扁形成3d错觉
  private static final float CENTERCONTENTOFFSET = 6;// 中间文字文字居中需要此偏移值
  private static final String GETPICKERVIEWTEXT = "getPickerViewText";// 反射的方法名

  //    boolean customTextSize;//自定义文字大小，为true则用于使setTextSize函数无效，只能通过xml修改
  public WheelView(Context context) {
    this(context, null);
  }

  public WheelView(Context context, AttributeSet attrs) {
    super(context, attrs);
    textColorOut = getResources().getColor(R.color.text_gray);
    textColorCenter = getResources().getColor(
        R.color.text_blue2);
    dividerColor = getResources().getColor(R.color.divider);
    //配合customTextSize使用，customTextSize为true才会发挥效果
//        customTextSize = getResources().getBoolean(R.bool.pickerview_customTextSize);
//        textSize = getResources().getDimensionPixelSize(R.dimen.pickerview_textsize);
    if (attrs != null) {
      TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.wheelview, 0, 0);
      mGravity = a.getInt(R.styleable.wheelview_gravity, Gravity.CENTER);
      textColorOut = a.getColor(R.styleable.wheelview_textColorOut, textColorOut);
      textColorCenter = a.getColor(R.styleable.wheelview_textColorCenter, textColorCenter);
      dividerColor = a.getColor(R.styleable.wheelview_dividerColor, dividerColor);
    }
    initLoopView(context);
  }

  private void initLoopView(Context context) {
    this.context = context;
    handler = new MessageHandler(this);
    gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this));
    gestureDetector.setIsLongpressEnabled(false);

    isLoop = true;
    textSize = 0;

    totalScrollY = 0;
    initPosition = -1;

    initPaints();

    setTextSize(10F);
  }

  private void initPaints() {
    paintOuterText = new Paint();
    paintOuterText.setColor(textColorOut);
    paintOuterText.setAntiAlias(true);
    paintOuterText.setTypeface(Typeface.MONOSPACE);
    paintOuterText.setTextSize(textSize);

    paintCenterText = new Paint();
    paintCenterText.setColor(textColorCenter);
    paintCenterText.setAntiAlias(true);
    paintCenterText.setTextScaleX(1.1F);
    paintCenterText.setTypeface(Typeface.MONOSPACE);
    paintCenterText.setTextSize(textSize);

    paintIndicator = new Paint();
    paintIndicator.setColor(dividerColor);
    paintIndicator.setAntiAlias(true);
    paintIndicator.setStrokeWidth(2);

    if (android.os.Build.VERSION.SDK_INT >= 11) {
      setLayerType(LAYER_TYPE_SOFTWARE, null);
    }
  }

  private void remeasure() {
    if (adapter == null) {
      return;
    }

    measureTextWidthHeight();

    // 最大Text的高度乘间距倍数得到 可见文字实际的总高度，半圆的周长
    halfCircumference = (int) (itemHeight * (itemsVisible - 1));
    // 整个圆的周长除以PI得到直径，这个直径用作控件的总高度
    measuredHeight = (int) ((halfCircumference * 2) / Math.PI);
    // 求出半径
    radius = (int) (halfCircumference / Math.PI);
    // 控件宽度，这里支持weight
    measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
    // 计算两条横线和控件中间点的Y位置
    firstLineY = (measuredHeight - itemHeight) / 2.0F;
    secondLineY = (measuredHeight + itemHeight) / 2.0F;
    centerY = (measuredHeight + maxTextHeight) / 2.0F - CENTERCONTENTOFFSET;
    // 初始化显示的item的position，根据是否loop
    if (initPosition == -1) {
      if (isLoop) {
        initPosition = (adapter.getItemsCount() + 1) / 2;
      } else {
        initPosition = 0;
      }
    }

    preCurrentIndex = initPosition;
  }

  /**
   * 计算最大len的Text的宽高度
   */
  private void measureTextWidthHeight() {
    Rect rect = new Rect();
    for (int i = 0; i < adapter.getItemsCount(); i++) {
      String s1 = getContentText(adapter.getItem(i));
      paintCenterText.getTextBounds(s1, 0, s1.length(), rect);
      int textWidth = rect.width();
      if (textWidth > maxTextWidth) {
        maxTextWidth = textWidth;
      }
      paintCenterText.getTextBounds("\u661F\u671F", 0, 2, rect); // 星期
      int textHeight = rect.height();
      if (textHeight > maxTextHeight) {
        maxTextHeight = textHeight;
      }
    }
    itemHeight = lineSpacingMultiplier * maxTextHeight;
  }

  void smoothScroll(ACTION action) {
    cancelFuture();
    if (action == ACTION.FLING || action == ACTION.DAGGLE) {
      mOffset = (int) ((totalScrollY % itemHeight + itemHeight) % itemHeight);
      if ((float) mOffset > itemHeight / 2.0F) {
        mOffset = (int) (itemHeight - (float) mOffset);
      } else {
        mOffset = -mOffset;
      }
    }
    // 停止的时候，位置有偏移，不是全部都能正确停止到中间位置的，这里把文字位置挪回中间去
    mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10,
        TimeUnit.MILLISECONDS);
  }

  protected final void scrollBy(float velocityY) {
    cancelFuture();

    mFuture = mExecutor
        .scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, VELOCITYFLING,
            TimeUnit.MILLISECONDS);
  }

  public void cancelFuture() {
    if (mFuture != null && !mFuture.isCancelled()) {
      mFuture.cancel(true);
      mFuture = null;
    }
  }

  /**
   * 设置是否循环滚动
   */
  public final void setCyclic(boolean cyclic) {
    isLoop = cyclic;
  }

  public final void setTextSize(float size) {
//        switch (type) {
//            case EnumUtil.PARK:
//                textSize =  DensityUtil.sp2px(context, 11);
//                break;
//            case EnumUtil.BIRTHDAY:
//                textSize =  DensityUtil.sp2px(context, 12);
//                break;
//            case EnumUtil.RANK:
//            case EnumUtil.HOLENUM:
//                textSize =  DensityUtil.sp2px(context, 14);
//                textColorCenter = ContextCompat.getColor(context, R.color.bg_blue);
//                paintCenterText.setColor(textColorCenter);
//                break;
//            default:
//                textSize = (int) size;
//                break;
//        }
    textSize = DensityUtil.sp2px(context, 14);
//    textSize = (int) size;
    paintOuterText.setTextSize(textSize);
    paintCenterText.setTextSize(textSize);

  }

  public final void setCurrentItem(int currentItem) {
    this.initPosition = currentItem;
    totalScrollY = 0;// 回归顶部，不然重设setCurrentItem的话位置会偏移的，就会显示出不对位置的数据
    invalidate();
  }

  public final void setOnItemSelectedListener(OnItemSelectedListener OnItemSelectedListener) {
    this.onItemSelectedListener = OnItemSelectedListener;
  }

  public final void setAdapter(WheelAdapter adapter) {
    this.adapter = adapter;
    remeasure();
    invalidate();
  }

  public final WheelAdapter getAdapter() {
    return adapter;
  }

  public final int getCurrentItem() {
    return selectedItem;
  }

  protected final void onItemSelected() {
    if (onItemSelectedListener != null) {
      postDelayed(new OnItemSelectedRunnable(this), 200L);
    }
  }

  @Override
  protected void onDraw(Canvas canvas) {
    if (adapter == null) {
      return;
    }
    // 可见的item数组
    Object visibles[] = new Object[itemsVisible];
    // 滚动的Y值高度除去每行Item的高度，得到滚动了多少个item，即change数
    change = (int) (totalScrollY / itemHeight);
    try {
      // 滚动中实际的预选中的item(即经过了中间位置的item) ＝ 滑动前的位置 ＋ 滑动相对位置
      preCurrentIndex = initPosition + change % adapter.getItemsCount();
    } catch (ArithmeticException e) {
      System.out.println("出错了！adapter.getItemsCount() == 0，联动数据不匹配");
    }
    if (!isLoop) {// 不循环的情况
      if (preCurrentIndex < 0) {
        preCurrentIndex = 0;
      }
      if (preCurrentIndex > adapter.getItemsCount() - 1) {
        preCurrentIndex = adapter.getItemsCount() - 1;
      }
    } else {// 循环
      if (preCurrentIndex < 0) {// 举个例子：如果总数是5，preCurrentIndex ＝
        // －1，那么preCurrentIndex按循环来说，其实是0的上面，也就是4的位置
        preCurrentIndex = adapter.getItemsCount() + preCurrentIndex;
      }
      if (preCurrentIndex > adapter.getItemsCount() - 1) {// 同理上面,自己脑补一下
        preCurrentIndex = preCurrentIndex - adapter.getItemsCount();
      }
    }

    // 跟滚动流畅度有关，总滑动距离与每个item高度取余，即并不是一格格的滚动，每个item不一定滚到对应Rect里的，这个item对应格子的偏移值
    int itemHeightOffset = (int) (totalScrollY % itemHeight);
    // 设置数组中每个元素的值
    int counter = 0;
    while (counter < itemsVisible) {
      int index = preCurrentIndex - (itemsVisible / 2
          - counter);// 索引值，即当前在控件中间的item看作数据源的中间，计算出相对源数据源的index值

      // 判断是否循环，如果是循环数据源也使用相对循环的position获取对应的item值，如果不是循环则超出数据源范围使用""空白字符串填充，在界面上形成空白无数据的item项
      if (isLoop) {
        if (index < 0) {
          index = index + adapter.getItemsCount();
          if (index < 0) {
            index = 0;
          }
        }
        if (index > adapter.getItemsCount() - 1) {
          index = index - adapter.getItemsCount();
          if (index > adapter.getItemsCount() - 1) {
            index = adapter.getItemsCount() - 1;
          }
        }
        visibles[counter] = adapter.getItem(index);
      } else if (index < 0) {
        visibles[counter] = "";
      } else if (index > adapter.getItemsCount() - 1) {
        visibles[counter] = "";
      } else {
        visibles[counter] = adapter.getItem(index);
      }
      counter++;

    }

    // 中间两条横线
    canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator);
    canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator);
    // 单位的Label
    if (label != null) {
      int drawRightContentStart = measuredWidth - getTextWidth(paintCenterText, label);
      // 靠右并留出空隙
      canvas.drawText(label, drawRightContentStart - CENTERCONTENTOFFSET, centerY, paintCenterText);
    }
    counter = 0;
    while (counter < itemsVisible) {
      canvas.save();
      // L(弧长)=α（弧度）* r(半径) （弧度制）
      // 求弧度--> (L * π ) / (π * r) (弧长X派/半圆周长)
      float itemHeight = maxTextHeight * lineSpacingMultiplier;
      double radian = ((itemHeight * counter - itemHeightOffset) * Math.PI) / halfCircumference;
      // 弧度转换成角度(把半圆以Y轴为轴心向右转90度，使其处于第一象限及第四象限
      float angle = (float) (90D - (radian / Math.PI) * 180D);
      // 九十度以上的不绘制
      if (angle >= 90F || angle <= -90F) {
        canvas.restore();
      } else {
        String contentText = getContentText(visibles[counter]);

        // 计算开始绘制的位置
        measuredCenterContentStart(contentText);
        measuredOutContentStart(contentText);
        float translateY = (float) (radius - Math.cos(radian) * radius
            - (Math.sin(radian) * maxTextHeight) / 2D);
        // 根据Math.sin(radian)来更改canvas坐标系原点，然后缩放画布，使得文字高度进行缩放，形成弧形3d视觉差
        canvas.translate(0.0F, translateY);
        canvas.scale(1.0F, (float) Math.sin(radian));
        if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) {
          // 条目经过第一条线
          canvas.save();
          canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY);
          canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT);
          canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
          canvas.restore();
          canvas.save();
          canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight));
          canvas.scale(1.0F, (float) Math.sin(radian) * 1F);
          canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET,
              paintCenterText);
          canvas.restore();
        } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) {
          // 条目经过第二条线
          canvas.save();
          canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY);
          canvas.scale(1.0F, (float) Math.sin(radian) * 1.0F);
          canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET,
              paintCenterText);
          canvas.restore();
          canvas.save();
          canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight));
          canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT);
          canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
          canvas.restore();
        } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) {
          // 中间条目
          canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
//                    - CENTERCONTENTOFFSET
          canvas.drawText(contentText, drawCenterContentStart, maxTextHeight, paintCenterText);
          int preSelectedItem = adapter.indexOf(visibles[counter]);
          if (preSelectedItem != -1) {
            selectedItem = preSelectedItem;
            System.out.println("selectitem" + selectedItem);
          }
        } else {
          // 其他条目
          canvas.save();
          canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
          canvas.scale(1.0F, (float) Math.sin(radian) * SCALECONTENT);
          canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
          canvas.restore();
        }
        canvas.restore();
      }
      counter++;
    }
  }

  /**
   * 根据传进来的对象反射出getPickerViewText()方法，来获取需要显示的值
   */
  private String getContentText(Object item) {
    String contentText = item.toString();
    try {
      Class<?> clz = item.getClass();
      Method m = clz.getMethod(GETPICKERVIEWTEXT);
      contentText = m.invoke(item, new Object[0]).toString();
    } catch (NoSuchMethodException e) {
    } catch (InvocationTargetException e) {
    } catch (IllegalAccessException e) {
    } catch (Exception e) {
    }
    if (Util.isNull(contentText)) {
      contentText = "";
    }
    return contentText;
  }

  private void measuredCenterContentStart(String content) {
    Rect rect = new Rect();
    paintCenterText.getTextBounds(content, 0, content.length(), rect);
    switch (mGravity) {
      case Gravity.CENTER:
        drawCenterContentStart = (int) ((measuredWidth - rect.width()) * 0.5);
        break;
      case Gravity.LEFT:
        drawCenterContentStart = 0;
        break;
      case Gravity.RIGHT:
        drawCenterContentStart = measuredWidth - rect.width();
        break;
    }
  }

  private void measuredOutContentStart(String content) {
    Rect rect = new Rect();
    paintOuterText.getTextBounds(content, 0, content.length(), rect);
    switch (mGravity) {
      case Gravity.CENTER:
        drawOutContentStart = (int) ((measuredWidth - rect.width()) * 0.5);
        break;
      case Gravity.LEFT:
        drawOutContentStart = 0;
        break;
      case Gravity.RIGHT:
        drawOutContentStart = measuredWidth - rect.width();
        break;
    }
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    this.widthMeasureSpec = widthMeasureSpec;
    remeasure();
    setMeasuredDimension(measuredWidth, measuredHeight);
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    boolean eventConsumed = gestureDetector.onTouchEvent(event);
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        startTime = System.currentTimeMillis();
        cancelFuture();
        previousY = event.getRawY();
        break;

      case MotionEvent.ACTION_MOVE:
        float dy = previousY - event.getRawY();
        previousY = event.getRawY();
        totalScrollY = (int) (totalScrollY + dy);

        // 边界处理。
        if (!isLoop) {
          float top = -initPosition * itemHeight;
          float bottom = (adapter.getItemsCount() - 1 - initPosition) * itemHeight;
          if (totalScrollY - itemHeight * 0.3 < top) {
            top = totalScrollY - dy;
          } else if (totalScrollY + itemHeight * 0.3 > bottom) {
            bottom = totalScrollY - dy;
          }

          if (totalScrollY < top) {
            totalScrollY = (int) top;
          } else if (totalScrollY > bottom) {
            totalScrollY = (int) bottom;
          }
        }
        break;

      case MotionEvent.ACTION_UP:
      default:
        if (!eventConsumed) {
          float y = event.getY();
          double l = Math.acos((radius - y) / radius) * radius;
          int circlePosition = (int) ((l + itemHeight / 2) / itemHeight);

          float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight;
          mOffset = (int) ((circlePosition - itemsVisible / 2) * itemHeight - extraOffset);

          if ((System.currentTimeMillis() - startTime) > 120) {
            // 处理拖拽事件
            smoothScroll(ACTION.DAGGLE);
          } else {
            // 处理条目点击事件
            smoothScroll(ACTION.CLICK);
          }
        }
        break;
    }
    invalidate();

    return true;
  }

  /**
   * 获取Item个数
   */
  public int getItemsCount() {
    return adapter != null ? adapter.getItemsCount() : 0;
  }

  /**
   * 附加在右边的单位字符串
   */
  public void setLabel(String label) {
    this.label = label;
  }

  public void setGravity(int gravity) {
    this.mGravity = gravity;
  }

  public int getTextWidth(Paint paint, String str) {
    int iRet = 0;
    if (str != null && str.length() > 0) {
      int len = str.length();
      float[] widths = new float[len];
      paint.getTextWidths(str, widths);
      for (int j = 0; j < len; j++) {
        iRet += (int) Math.ceil(widths[j]);
      }
    }
    return iRet;
  }
}